///////////////////////////////////////////////////////////////////////////////
//
// lighthouse3D - 3D Software Library
//
// File: lh3dappmain.cpp
//
///////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2003-2013 Marcus Reinhardt
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
///////////////////////////////////////////////////////////////////////////////
//
// You can find a copy of the GNU General Public License (gpl.txt) in the
// directory of the library.
//
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
//
// This file is the starting point for a new application using lighthouse 3D
//
///////////////////////////////////////////////////////////////////////////////

#include "lh3dappmain.h"
#include "C3DLibrary.h"
#include "C3DLibInterface.h"

// Library Refernce
C3DLibrary *pLighthouse3D;
// Interface Instance
C3DLibInterface lighthouse3DIF;

TAppLibrarySetup tALS;

TEnvInformation *tLocalEI;

// GUI Element Handles
TGUIHandle *mainHandleUI;
TGUIHandle *windowHandle; 
TGUIHandle *buttonHandle1, *buttonHandle2;
TGUIHandle *labelHandle1, *labelHandle2, *labelHandle3, *labelHandle4;

char requestScreenUpdate = 1; // Initially update screen once
int  lastMouseX = 0, lastMouseY = 0;
int  lastLeftMousePressed, lastMiddleMousePressed, lastRightMousePressed;

///////////////////////////////////////////////////////////////////////////////
// lh3dappmain_event_setup( ... )
///////////////////////////////////////////////////////////////////////////////
//
// Event Task:
//
// This event is happening once at very beginning of the application and allows
//
// to override the initial renderer setup. In case a NULL ptr is returned the
//
// settings will not be changed.
//
///////////////////////////////////////////////////////////////////////////////
TAppLibrarySetup *lh3dappmain_event_setup(TEnvInformation *tEI)
{
   // Prepare init structure for renderer
   tALS.renderMode      = 2; // Texture Render enabled
   tALS.shadingMode     = 3; // Gouraud Shading enabled
   tALS.lightingMode    = 1; // Lambert Lighting enabled
   tALS.fsaaliasMode    = 0; // FSAA disabled
   tALS.texFilterMode   = 0; // Texture filter disabled
   tALS.bfcullingMode   = 1; // Back face enabled

#ifndef __MULTI_CORE_SUPPORT
   // Run library single threaded
   tALS.nrWorkerThreads = 0; 
#else
   // Create as much workers as cores if supported by the library
   if (tEI->nrCPUCores < __CPU_CORES_SUPPORTED)
     tALS.nrWorkerThreads = tEI->nrCPUCores;   
   else
     tALS.nrWorkerThreads = __CPU_CORES_SUPPORTED;   
#endif
   // Keep reference to system environment description
   tLocalEI = tEI;

   // Return pointer to init structure (a NULL ptr won't change the defaults)
   return &tALS;
}

///////////////////////////////////////////////////////////////////////////////
// lh3dappmain_event_init( ... )
///////////////////////////////////////////////////////////////////////////////
//
// Event Task:
//
// The event is fired only once after the lighthouse renderer is created and
//
// basically initiallized. The application receives the reference to the
//
// renderer which needs to be kept for later tasks.
//
// Additionally here an initial scenary can be created, the font loaded,
//
// background picture loaded and so on.
//
///////////////////////////////////////////////////////////////////////////////
unsigned char lh3dappmain_event_init(C3DLibrary *pLibReference)
{
	// Keep reference to library for later usage
	pLighthouse3D = pLibReference;

	// Bind library to interface
	lighthouse3DIF.bindLibrary(pLighthouse3D);

	// Load initial font
	lighthouse3DIF.loadFont("../resources/basicfont.bmp");

	// Add ambient light to scene
	lighthouse3DIF.setAmbientLight(0.3);

	// Add light source to scene
	lighthouse3DIF.addDotLight(500, 500, -500, 255, 255, 255, 0.7, 0.7);

	// Add camera and activate it
	lighthouse3DIF.addCamera(0, 0, 0, 0, 0, -150, 1, 1020);
	lighthouse3DIF.setActiveCamera(0);

	// Re-place camera to position somewhere in the scene
	lighthouse3DIF.moveAheadCamera(170);
	lighthouse3DIF.moveUpCamera(280);
	lighthouse3DIF.turnDownCamera(1);
	lighthouse3DIF.moveRightCamera(530);

	// Set transparency for all UI elements
	lighthouse3DIF.setGUITransparency(0.8);

	// Setup collision detection
	lighthouse3DIF.setupColDetection(_standartCollisionDetection_);
	lighthouse3DIF.setColDistance(40);

	// Setup the fog parameters
	lighthouse3DIF.setFogSynthesis(200, 980, 0.05, 215, 215, 215);
	lighthouse3DIF.setupFogSynthesis(_linearFogSynthesis_);

    // Setup height map object factory (scaleX = 15, scaleY = 1.5, autobalance scaleX = true);
	lighthouse3DIF.setHeightMapParams(15, 1.5, 1); //
	// Create the surface
	if (!lighthouse3DIF.createHeightMapSurface("../resources/terrain_1.bmp", "../resources/tex_land_1.bmp")) {
		// Error: Could not load one or both files
		return 0;
	}

	// Set a background picture (its gray because we use fog synthesis)
	if (!lighthouse3DIF.setBackgroundPicture("../resources/fog.bmp")) {
		// Error: Could not load one or both files
		return 0;
	}

    // Save UI Main Handle
	mainHandleUI = lighthouse3DIF.getMainHandle();

	// Create UI elements
	windowHandle  = lighthouse3DIF.createWindow(mainHandleUI, 10, 10, 160, 100, 16, "World Infos");
	buttonHandle1 = lighthouse3DIF.createButton(mainHandleUI, 10, tALS.screenHeight-32, 80, 22, "Exit");
	buttonHandle2 = lighthouse3DIF.createButton(mainHandleUI, 10, tALS.screenHeight-62, 80, 22, "Snaphot");
	labelHandle1  = lighthouse3DIF.createLabel(windowHandle, 6, 20, 0, "Vertexes:");
	labelHandle2  = lighthouse3DIF.createLabel(windowHandle, 6, 40, 0, "Faces:");
	labelHandle3  = lighthouse3DIF.createLabel(windowHandle, 6, 60, 0, "Reendered:");
#ifdef __MULTI_CORE_SUPPORT
	sprintf(tempStr, "Multi-Core Application (%d:%d)", tALS.nrWorkerThreads, tLocalEI->nrCPUCores);
	labelHandle4  = lighthouse3DIF.createLabel(windowHandle, 6, 80, 0, tempStr);
#else
	labelHandle4  = lighthouse3DIF.createLabel(windowHandle, 6, 80, 0, "Single-Core Application");
#endif

	// Ok
	return 1;
}

///////////////////////////////////////////////////////////////////////////////
// lh3dapplication_main_task( ... )
///////////////////////////////////////////////////////////////////////////////
//
// Event Task:
//
// This event is created periodically (around 100 times per second). So if you
//
// want to update ojects (e.g. their position or structure) you could do it
//
// here.
//
// The return value of 0 indicates that no screen update currently is necessary.
//
// A value of 1 requests an immendiate update.
//
///////////////////////////////////////////////////////////////////////////////
unsigned char lh3dappmain_event_task(void)
{
	char tmpString[32];
	char tmpScreenUpdateRequest = requestScreenUpdate;
	
	// Clear screen update request, otherwise we provoke heavy CPU load
	requestScreenUpdate = 0;

	// If we update the screen we update the UI elements, too)
	if (tmpScreenUpdateRequest) {
  	  sprintf(tmpString, "Vertexes: %i", lighthouse3DIF.getVertexCount());
	  lighthouse3DIF.setCaption(labelHandle1, tmpString);

	  sprintf(tmpString, "Faces: %i", lighthouse3DIF.getTrianglesCount());
	  lighthouse3DIF.setCaption(labelHandle2, tmpString);

	  sprintf(tmpString, "Rendered: %i", lighthouse3DIF.getRenderedTrianglesCount());
	  lighthouse3DIF.setCaption(labelHandle3, tmpString);
	}

	// 0 = Update not necessary, 1 = Update necessary
	return tmpScreenUpdateRequest;
}

///////////////////////////////////////////////////////////////////////////////
// lh3dappmain_event_resize( ... )
///////////////////////////////////////////////////////////////////////////////
//
// Event Task:
//
// This event is thrown in case the screen size was changed. Whatever is done
//
// here (like repositioning UI elements): the screen needs an update by then.
//
///////////////////////////////////////////////////////////////////////////////
unsigned char  lh3dappmain_event_resize(int width, int height)
{
	// Keep in mind we need to request a screen update
	requestScreenUpdate = 1;

	// Ok
	return 1;
}

///////////////////////////////////////////////////////////////////////////////
// lh3dappmain_event_mousechanged( ... )
///////////////////////////////////////////////////////////////////////////////
//
// Event Task:
//
// This function implements the mouse handler. In case the left mouse button,
//
// the middle mouse button or the right mouse button was pressed or released
//
// this function is called having inside the most recent mouse coordinates.
//
// The function is also called in case only position of the mouse recently
//
// changed.
//
///////////////////////////////////////////////////////////////////////////////
unsigned char  lh3dappmain_event_mousechanged(int leftMousePressed, int middleMousePressed, int rightMousePressed, int horzPos, int vertPos)
{	
	int curMouseX = horzPos;
	int curMouseY = vertPos;
	int tmpX = 0, tmpY = 0;

	// First test for UI elements
	if ( (leftMousePressed == 1) && (lastLeftMousePressed == 0) ) {
		// Check if we clicked to the exit button
		if (lighthouse3DIF.isOverGUIElement(buttonHandle1, horzPos, vertPos)) {
			// leave application
			exit(0);
		}

		// Check if we clicked to the snapshot button
		if (lighthouse3DIF.isOverGUIElement(buttonHandle2, horzPos, vertPos)) {
			lighthouse3DIF.saveSnapshot("snapshot.tga");

			// Immediatly leave with Ok!
			return 1;
		}
	}

	// Second move along

	// left mouse handler: move camera along current camera view vector
	if (leftMousePressed == 1) {
		// Move ahead camera
		lighthouse3DIF.moveAheadCamera(3);

    	// Keep in mind we need to request a screen update
		requestScreenUpdate = 1;
	}

	// middle mouse handler: translate camera view vector
	if (middleMousePressed == 1) {
		// First time we only save the coordinates
		if (lastMouseX != 0) {
		  if (curMouseX < lastMouseX)
			tmpX = abs(curMouseX-lastMouseX)/3;
		  if (curMouseX > lastMouseX)
			tmpX = -abs(curMouseX-lastMouseX)/3;
		  if (curMouseY > lastMouseY)
  			tmpY = abs(curMouseY-lastMouseY)/3;
	  	  if (curMouseY < lastMouseY)
			tmpY = -abs(curMouseY-lastMouseY)/3;

		  if (tmpX != 0)
  		    lighthouse3DIF.moveLeftCamera((float) tmpX);
		  if (tmpY != 0)
		    lighthouse3DIF.moveDownCamera((float) tmpY);
		}

    	// Keep in mind we need to request a screen update
		requestScreenUpdate = 1;
	}

	// right mouse handler: rotate camera view vector
	if (rightMousePressed == 1) {
		// First time we only save the coordinates
		if (lastMouseX != 0) {
		  if (curMouseX < lastMouseX)
			tmpX = abs(curMouseX-lastMouseX)/3;
		  if (curMouseX > lastMouseX)
			tmpX = -abs(curMouseX-lastMouseX)/3;
		  if (curMouseY > lastMouseY)
  			tmpY = abs(curMouseY-lastMouseY)/3;
	  	  if (curMouseY < lastMouseY)
			tmpY = -abs(curMouseY-lastMouseY)/3;

		  if (tmpX != 0)
  		    lighthouse3DIF.turnLeftCamera(tmpX);
		  if (tmpY != 0)
  		    lighthouse3DIF.turnDownCamera(tmpY);
		}

    	// Keep in mind we need to request a screen update
		requestScreenUpdate = 1;
	}

	// Save old coordinates
	lastMouseX = curMouseX;
	lastMouseY = curMouseY;

    // Save old mouse states
	lastLeftMousePressed = leftMousePressed;
	lastMiddleMousePressed = middleMousePressed;
	lastRightMousePressed = rightMousePressed;

	// Ok
	return 1;
}
